package ThreadBaseTest;

/**
 * Created by yexuan on 2018/3/7.
 */

/*
1.增加一个知识点
一个类怎么在所有的类中，让其它类来共同修改它的数据呢？
可以用单例设计模式
可以用静态
可以在其它类中做一个构造函数，接受同一个对象，这样就可以实现对象

2.状态选择
可以用数字0 1 判断
可以用bool
注意变量的范围即可

3.加了同步后，还是有安全怎么办？
想前提！  1.  两个及以上线程（同步的）  2.操作公用资源  3.要用同一锁
*/

/*
线程间通讯：
其实就是多个线程在操作同一个资源，
但是操作的动作不同。

*/



/*
1.为什么会出现安全问题？
就是不满足前提呗

2.我们还没有了解到线程的本质
1.线程的执行时互相争抢执行权的
2.如果没有同步代码块的话，就会对数据进行胡乱修改，有可能修改到一半，另一个线程进来，有可能修改好几次，等等，甚至还会无视条件
3.即便有了同步代码块，也不能保证代码是按次序相互执行，因为，第一个线程执行完之后，还会参与下一次的争抢当中来，拥有相同的争抢概率
所以就出现同步中的不同步状况，这时候就要用到唤醒
4.其实程序就是一个逻辑判断问题，就是看有没有数据
*/


/*五种状态之一的等待
1.是Thread从上帝那里继承来的
2.而且这个函数是抛了异常的，根据异常的格式，要try和catch
3.唤醒也是继承上帝的
4.使用的时候，直接用对象点出来就可以
5.这样很容易区分，也造成为什么要把这两个函数写在上帝里面的原因，锁是任意类型的，什么对象都可以

*/

class Res                                   /*定义一个类，里面封装好数据成员，用来去调用*/
{
    String name;
    String sex;
    boolean flag = false;      /*用来判断是否已经存入数据，封装好用来调用*/
}

class Input implements Runnable
{
    private Res r ;
    Input(Res r)
    {
        this.r = r;
    }
    public void run()
    {
        int x = 0;
        while(true)
        {
            synchronized(r)             /*只能让一个线程进来，而且输入的先进来，输出的就不给进了，这样就满足了前提，有两个线程，同一锁，公共资源*/
            {

                if(r.flag)               /*真，冻结*/
                    try{r.wait();}catch(Exception e){}     /*线程池，等待线程都在线程池当中，唤醒的是什么，唤醒的都是线程池当中的线程*/
                if(x==0)
                {
                    r.name="mike";                    /*赋值*/
                    r.sex="man";
                }
                else
                {
                    r.name="丽丽";
                    r.sex = "女女女女女";
                }
                x = (x+1)%2;               /*转换性别*/
                r.flag = true;
                r.notify();       /*有就唤醒，没有就不唤醒，按顺序来自然就不需要这个，唤醒的是另一个线程*/
            }
        }
    }
}

class Output implements Runnable
{
    private Res r ;            /*对象数据成员*/

    Output(Res r)
    {
        this.r = r;                  /*传递对象进来*/
    }
    public void run()
    {
        while(true)
        {
            synchronized(r)             /*放的是对象*/
            {
                if(!r.flag)
                    try{r.wait();}catch(Exception e){}
                System.out.println(r.name+"...."+r.sex);            /*删除*/
                r.flag = false;
                r.notify();                                 /*唤醒输入线程*/
            }
        }
    }
}


class  InputOutputDemo
{
    public static void main(String[] args)
    {
        Res r = new Res();                     /*封装好的类，将会当作参数，传递给其它类*/

        Input in = new Input(r);               /*实现多线程类的对象*/
        Output out = new Output(r);

        Thread t1 = new Thread(in);          /*开启线程，把实现线程的对象放进来*/
        Thread t2 = new Thread(out);

        t1.start();                  /*开启线程，调用run函数*/
        t2.start();
    }
}

/*
wait:
notify();
notifyAll();

都使用在同步中，因为要对持有监视器(锁)的线程操作。
所以要使用在同步中，因为只有同步才具有锁。

为什么这些操作线程的方法要定义Object类中呢？
因为这些方法在操作同步中线程时，都必须要标识它们所操作线程只有的锁，
只有同一个锁上的被等待线程，可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。

也就是说，等待和唤醒必须是同一个锁。

而锁可以是任意对象，所以可以被任意对象调用的方法定义Object类中。

*/